/************************************************************************

                                                                Heroes IV
                                          Copyright 2000, The 3DO Company

        ------------------------------------------------------------------
                                                         font.h

        $Header: /resource_editor/font.h $

        $NoKeywords: $

 ************************************************************************/

// kill the 255+ character identifier warning.  Templates tend to exceed that easily.
#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "font.h"

#include "alpha_blend.h"
#include "streambuf_operators.h"
#include "string_vector.h"

char const t_resource_traits<t_font>::prefix[] = "font";

char const t_resource_traits<t_font>::extension[] = "fnt";

// -------------------------------------------------------------
// bitmap class for holding character image for a font
// -------------------------------------------------------------
void t_font_bitmap::create( int width, int height, int leading_space, int trailing_space )
{
        m_leading_space = leading_space;
        m_trailing_space = trailing_space;
        m_image.resize( width * height );
        init( width, height, width, &(*m_image.begin()) );
}

// -------------------------------------------------------------
// bitmap class for holding character image for a font
// -------------------------------------------------------------
void t_font_bitmap::draw_to( t_abstract_bitmap16& bitmap, t_screen_point point, t_uint16 color,
                                                         bool drop_shadow, t_uint16 drop_shadow_color ) const
{
        t_screen_rect  dest_rect = intersection( get_rect() + point, bitmap.get_rect() );

        if (dest_rect.height() <= 0 || dest_rect.width() <= 0)
                return;

        t_screen_point source_point = dest_rect.top_left() - point;
        t_uint16*      ptr = bitmap.advance_line( bitmap.get_data_ptr(), dest_rect.top ) 
                                 + dest_rect.left;
        t_uint16       pixel;
        const t_uint8* source = advance_line( get_data_ptr(), source_point.y ) + source_point.x;
    int            width = dest_rect.width();
        int            height = dest_rect.height();
        int            increment = get_pitch() - width * sizeof(t_uint8);
        int            dest_increment = bitmap.get_pitch() - width * sizeof(t_uint16);
        int            i;

        while (height--)
        {
                i = width;
                while (i--)
                {
                        if (drop_shadow)
                                pixel = alpha_blend16( *ptr, drop_shadow_color, (*source) & 0xF );
                        else
                                pixel = *ptr;
                        *ptr = alpha_blend16( pixel, color, (*source) >> 4 );
                        source++;
                        ptr++;
                }
                source = byte_increment( source, increment );
                ptr = bitmap.byte_increment( ptr, dest_increment );
        }
}

// -------------------------------------------------------------
// bitmap class for holding character image for a font
// -------------------------------------------------------------
bool t_font_bitmap::read( std::streambuf& buffer, int version )
{
        int width;
        int height;
        int size;

        buffer >> width >> height >> m_leading_space >> m_trailing_space;
        size = width * height;
        m_image.resize( size );
        init( width, height, width, &(*m_image.begin()) );
        ::read( buffer, &(*m_image.begin()), size );
        if (version == 0)
        {
                // convert from transparent / shadow / foreground encoding to alpha level encoding.
                std::vector<t_uint8>::iterator index = m_image.begin();

                while (index != m_image.end())
                {
                        switch (*index)
                        {
                                case 1:
                                        *index = 0xF;
                                        break;

                                case 2:
                                        *index = 0xF0;
                                        break;
                        }
                        index++;
                }
        }
        return true;
}

// -------------------------------------------------------------
// bitmap class for holding character image for a font
// -------------------------------------------------------------
bool t_font_bitmap::write( std::streambuf& buffer ) const
{
        buffer << get_width() << get_height() << m_leading_space << m_trailing_space;
        ::write( buffer, &(*m_image.begin()), get_width() * get_height() );
        return true;
}

// -------------------------------------------------------------
// font class
// -------------------------------------------------------------
t_font::t_font()
{
        m_size = 0;
        m_characters = 0;

        m_hDC   = 0;
        m_hFont = 0;
        m_hbkFont = 0;

}


// -------------------------------------------------------------
// font class
// -------------------------------------------------------------
t_font::t_font( int first_character, int height, int max_width, int baseline, int size )
      : m_first_character( first_character ), m_height( height ), m_max_width(max_width),
            m_size(size), m_characters( new t_font_bitmap[size] ), m_baseline( baseline )
{
        m_hDC   = 0;
        m_hFont = 0;
        m_hbkFont = 0;

        for(int i=0;i<50;i++)
        {
            m_pown[i]   = 0;
        }
}

// -------------------------------------------------------------
// font class
// -------------------------------------------------------------
t_font::~t_font()
{
        if (m_characters != 0)
                delete [] m_characters;
}




// -------------------------------------------------------------
// font class
// -------------------------------------------------------------
void t_font::draw_to( t_abstract_bitmap16& bitmap, t_screen_point point,
                              char const* text, t_uint16 color, bool drop_shadow,
                                          t_uint16 drop_shadow_color, UINT pown, int line) 
{
        t_font_bitmap* character;
        bool           first = true;
        bool               usebuffer    = false;
        bool               modifybuffer = true;


        if(pown && line<50)
        {
                usebuffer       = true;
                if(pown==m_pown[line] )
                {
                        modifybuffer    = false;
                }
                else
                {
                        m_pown[line]    = pown;
                }
        }

        int             i=0;
        while (*text != 0)
        {                
                {
                        character = get_character( *text );
                        
                        if (!first)
                        {
                                point.x += character->get_leading_space();
                        }
                        
                        first = false;
                        
                        character->draw_to( bitmap, point, color, drop_shadow, drop_shadow_color );
                        point.x += character->get_width() + character->get_trailing_space();
                }
                
                text++;
                i++;
        }


}
/*
// -------------------------------------------------------------
// font class
// -------------------------------------------------------------
void t_font::draw_to( t_abstract_bitmap16& bitmap, t_screen_point point,
                              char const* text, t_uint16 color, bool drop_shadow,
                                          t_uint16 drop_shadow_color ) const
{
        t_font_bitmap* character;
        bool           first = true;

        while (*text != 0)
        {
                character = get_character( *text );
                if (!first)
                        point.x += character->get_leading_space();
                first = false;
                character->draw_to( bitmap, point, color, drop_shadow, drop_shadow_color );
                point.x += character->get_width() + character->get_trailing_space();
                text++;
        }
}
*/

// -------------------------------------------------------------
// font class
// -------------------------------------------------------------
int t_font::get_width( char const* text ) const
{
	t_font_bitmap* character;
	bool           first = true;
	int            result = 0;

	while (*text != 0)
	{
		{
			character = get_character( *text );
			if (!first)
				result += character->get_leading_space();
			first = false;
			result += character->get_width();
			if (text[1] != 0)
				result += character->get_trailing_space();
		}

		text++;
	}
	return result;
}

// -------------------------------------------------------------
// font class
// -------------------------------------------------------------
int t_font::get_column( char const* text, int x ) const
{
	t_font_bitmap* character;
	bool           first = true;
	int            char_width;
	int            width = 0;
	int            column = 0;

	while (*text != 0)
	{
		{
			character = get_character( *text );
			char_width = 0;
			if (!first)
				char_width += character->get_leading_space();
			first = false;
			char_width += character->get_width();
			if (text[1] != 0)
				char_width += character->get_trailing_space();
			width += char_width;
			if (width >= x)
			{
				if (width - x < char_width / 2)
					return column + 1;
				return column;
			}
		}
		
		column++;
		text++;
	}

	return column;
}

// -------------------------------------------------------------
// font class
// -------------------------------------------------------------
bool t_font::read( std::streambuf& buffer )
{
        int i;

        int version = get<t_uint16>(buffer);

        if (version >= 0x100) // we are actually reading version 0, prior to version signature
        {
                m_first_character = version & 0xFF;
                m_height = version >> 8;
                version = 0;
        }
        else
        {
                m_first_character = buffer.sbumpc();
                m_height = buffer.sbumpc();
        }
        m_max_width = buffer.sbumpc();
        m_baseline = buffer.sbumpc();
        m_size = buffer.sbumpc();

	if (m_characters != 0)
		delete m_characters;

	m_characters = new t_font_bitmap[m_size];
	if (!m_characters)
		return false;

	for (i = 0; i < m_size; i++)
		if (!m_characters[i].read( buffer, version ))
			return false;

	return true;
}

// -------------------------------------------------------------
// font class
// -------------------------------------------------------------
const int k_version = 1;

bool t_font::write( std::streambuf& buffer ) const
{
        int i;

        put<t_uint16>( buffer, k_version );
        buffer.sputc( m_first_character );
        buffer.sputc( m_height );
        buffer.sputc( m_max_width );
        buffer.sputc( m_baseline );
        buffer.sputc( m_size );
        for (i = 0; i < m_size; i++)
                if (!m_characters[i].write( buffer ))
                        return false;
        return true;
}

// ---------------------------------------------------------------
// wrap text to a given width, but allow unlimited height.
// ---------------------------------------------------------------
int t_font::wrap_text( t_string_vector& result, char const* text, int width ) const
{
	std::string line;               // current line
	std::string word;               // current word
	int         line_width = 0;     // current line width
	int         leading_space;      // leading space required to add new string to line
	int         trailing_space = 0; // trailing space of last character
	int         word_width;
	int         widest_line = 0;
	char const* word_start;
	t_font_bitmap* space_bitmap = get_character( ' ' );
	
	result.clear();
	assert( width > 0 );
	while (*text != 0)
	{
		word = "";
		word_width = 0;
		// to add to the current line, we must include trailing space for the last character.
		leading_space = trailing_space;
		// first character of new word, so no trailing space required.
		trailing_space = 0;
		word_start = text;
		while (*text == ' ')
		{
			// if word is non-zero in length, include the leading space.
			if (word_width >  0)
				word_width += space_bitmap->get_leading_space();
			else if (line_width > 0)
				leading_space += space_bitmap->get_leading_space(); // else add to inter-word space.
			word_width += trailing_space;// include trailing space from previous char
			word_width += space_bitmap->get_width(); // include character width
			trailing_space = space_bitmap->get_trailing_space(); // note space required to add another char to word
			word += *text++;						   // add char to word.
		}	
		if (*text == '\n')		// new line
		{
			if (line_width > widest_line)
				widest_line = line_width;
			text++;				// skip newline character
			result.push_back( line );	// add current line to result
			line = "";				   // set up for new line.
			line_width = 0;
			trailing_space = 0;
			continue;
		}
		while (*text != 0 && *text != ' ' && *text != '\n')
		{
			{
				t_font_bitmap* bitmap = get_character( *text );
				if (word_width >  0)						// if word is started, include leading space
					word_width += bitmap->get_leading_space(); 
				else if (line_width > 0)					// else add leading space to inter-word space.
					leading_space += bitmap->get_leading_space();
				word_width += trailing_space;				// add trailing space of last char
				word_width += bitmap->get_width();				// add char width
				trailing_space = bitmap->get_trailing_space();	// record trailing space of this char
				word += *text++;							// add to word
			}
		}
		if (line_width + leading_space + word_width <= width)
		{
			line_width += word_width + leading_space;	// word fits on line, so simply add to line.
			line += word;
			continue;
		}
		if (line_width > 0)			// if line has been started, push it back.
		{
			if (line_width > widest_line)
				widest_line = line_width;
			result.push_back( line );
			while (!word.empty() && word[0] == ' ')	     	// if word includes leading space, eat it.
			{
				word_width -= space_bitmap->get_width();		// remove the space's width.
				word.erase( 0, 1 );
				if (word.length() > 0)
				{
					word_width -= space_bitmap->get_trailing_space();
				}
			}
		}
		while (word_width >= width && word.length() > 1)		// if word is longer than a line
		{
			{
				text--;						// back up until we can fit on a line.
				
				t_font_bitmap* bitmap = get_character( *text );
				
				word.erase( word.length() - 1, 1 );
				word_width -= bitmap->get_width() + bitmap->get_leading_space();	// remove space + leading space
				trailing_space = get_character( text[-1] )->get_trailing_space(); // remove next char's trailing space.
				word_width -= trailing_space;
			}
		}
		line = word;				// line becomes new word
		line_width = word_width;
	
	}
	if (line_width > widest_line)
		widest_line = line_width;
	if (line_width > 0)			// if we have any left over line, include it.
		result.push_back( line );
	return widest_line;
}

// ---------------------------------------------------------------
// compute length of longest word in a string
// ---------------------------------------------------------------
int t_font::longest_word_length( char const* text ) const
{
	int result = 0;
	int length;

	while (*text != 0)
	{
		while (*text == ' ' || *text == '\n')
			text++;
		length = 0;
		while (*text != 0 && *text != ' ' && *text != '\n')
		{
			t_font_bitmap* bitmap ;

			{
				bitmap = get_character( *text );
				
				if (length > 0)
					length += bitmap->get_leading_space();
				length += bitmap->get_width() + bitmap->get_trailing_space();
				text++;
			}
		}
		if (length > result)
			result = length;
	}
	return result;
}

// ---------------------------------------------------------------
// compute best rectangle to display text
// ---------------------------------------------------------------
int t_font::wrap_text( t_string_vector& result, char const* text, int width, int max_width ) const
{
	int minimum_width = longest_word_length( text );
	int actual_width;
	int height;

	assert( max_width > 0 );
	if (max_width < 0)
		max_width = 0;
	if (minimum_width > max_width)
		minimum_width = max_width;
	if (width < minimum_width)
		width = minimum_width;
	while (true)
	{
		actual_width = wrap_text( result, text, width );
		if (result.size() == 1)
			return actual_width;
		height = result.size() * m_height;
		if (width >= max_width || height * 4 <= width)
			return actual_width;
		width = width * 3 / 2;
		if (width > max_width)
			width = max_width;
	}
}

// ---------------------------------------------------------------
// draw pre-wrapped text to a given rectangle
// ---------------------------------------------------------------
void t_font::draw_to( t_abstract_bitmap16& bitmap, t_screen_rect const& rect, 
		              t_string_vector const& text_block, t_uint16 color, bool drop_shadow,
					  t_uint16 drop_shadow_color ) const
{
	t_string_vector::const_iterator index;
	t_screen_point                  point = rect.top_left();
	t_font_bitmap*                  character;

	if (rect.width() <= 0 || rect.height() <= 0)
		return;

	for (index = text_block.begin(); index != text_block.end(); index++)
	{
		if (point.y + m_height > rect.bottom)
			return;

		bool first = true;
		char const* text = index->c_str();

		while (*text != 0)
		{
			character = get_character( *text );
			if (!first)
				point.x += character->get_leading_space();
			first = false;
			if (point.x + character->get_width() > rect.right)
				break;
			character->draw_to( bitmap, point, color, drop_shadow, drop_shadow_color );
			point.x += character->get_width() + character->get_trailing_space();
			text++;
		}
		point.x = rect.left;
		point.y += m_height;
	}
}

// -----------------------------------------------------------------
// draw text, wrapped to fit the rectangle
// -----------------------------------------------------------------
void t_font::draw_to( t_abstract_bitmap16& bitmap, t_screen_rect const& rect, char const* text,
		              t_uint16 color, bool drop_shadow, t_uint16 drop_shadow_color ) const
{
	t_string_vector text_block;

//	m_bfirst	= TRUE;
	wrap_text( text_block, text, rect.width() );
	draw_to( bitmap, rect, text_block, color, drop_shadow, drop_shadow_color );
}

int t_font::get_wrapped_height( char const* text, int width ) const
{
	t_string_vector text_block;

	wrap_text( text_block, text, width );
	return text_block.size() * m_height;
}
